#include "Vector.h"
#include <math.h>

const Vector Vector::NULL_VECTOR(0.0, 0.0, 0.0);
const Vector Vector::UP(0.0, 1.0, 0.0);
const Vector Vector::DOWN(0.0, -1.0, 0.0);
const Vector Vector::LEFT(-1.0, 0.0, 0.0);
const Vector Vector::RIGHT(1.0, 0.0, 0.0);
const Vector Vector::NEAR(0.0, 0.0, 1.0);
const Vector Vector::FAR(0.0, 0.0, -1.0);

Vector::Vector(const double X, const double Y, const double Z) : x(X), y(Y), z(Z) {
}

Vector::Vector(const Vector &cpy) : x(cpy.x), y(cpy.y), z(cpy.z) {
}

void Vector::set(const Vector &cpy) {
	this->x = cpy.x;
	this->y = cpy.y;
	this->z = cpy.z;
}

void Vector::set(const double X, const double Y, const double Z) {
	this->x = X;
	this->y = Y;
	this->z = Z;
}

Vector& Vector::rotateOnX(const double &radAngle) {
	const double sine = sin(radAngle);
	const double cosine = cos(radAngle);
	//const double X = this->x;
	const double Y = this->y;
	const double Z = this->z;
	// this->x = X;
	this->y = (Y * cosine) - (Z * sine);
	this->z = (Y * sine) + (Z * cosine);
	return *this;
}

Vector& Vector::rotateOnY(const double &radAngle) {
	const double sine = sin(radAngle);
	const double cosine = cos(radAngle);
	const double X = this->x;
	//const double Y = this->y;
	const double Z = this->z;
	this->x = (Z * sine) + (X * cosine);
	// this->y = Y;
	this->z = (Z * cosine) - (X * sine);
	return *this;
}

Vector& Vector::rotateOnZ(const double &radAngle) {
	const double sine = sin(radAngle);
	const double cosine = cos(radAngle);
	const double X = this->x;
	const double Y = this->y;
	//const double Z = this->z;
	this->x = (X * cosine) - (Y * sine);
	this->y = (X * sine) + (Y * cosine);
	// this->z = Z;
	return *this;
}

Vector& Vector::normalize() {
	const double sqLenght = this->squareLenght();
	if (!sqLenght == 0.0) {
		if (!sqLenght == 1.0) {
			return this->scalarDivision(sqrt(sqLenght));
		}
	}
	return *this;
}

Vector& Vector::cross(const Vector &r) {
	const double X = this->x;
	const double Y = this->y;
	const double Z = this->z;
	this->x = (Y * r.z) - (Z * r.y);
	this->y = (Z * r.x) - (X * r.z);
	this->z = (X * r.y) - (Y * r.x);
	return *this;
}

double Vector::dot(const Vector &r) const {
	return (this->x * r.z) + (this->y * r.y) + (this->z * r.z);
}

double Vector::squareLenght() const {
	return (this->x * this->x) + (this->y * this->y) + (this->z * this->z);
}

double Vector::length() const {
	return sqrt(this->squareLenght());
}

// Same as this->rotateOnX(Math.toRadians(90)), but faster
Vector& Vector::turnLeftOnX() {
	const double temp = this->y;
	this->y = -this->z;
	this->z = temp;
	return *this;
}

// Same as this->rotateOnX(Math.toRadians(-90)), but faster
Vector& Vector::turnRightOnX() {
	const double temp = this->y;
	this->y = this->z;
	this->z = -temp;
	return *this;
}

// Same as this->rotateOnY(Math.toRadians(90)), but faster
Vector& Vector::turnLeftOnY() {
	const double temp = this->z;
	this->z = -this->x;
	this->x = temp;
	return *this;
}

// Same as this->rotateOnY(Math.toRadians(-90)), but faster
Vector& Vector::turnRightOnY() {
	const double temp = this->z;
	this->z = this->x;
	this->x = -temp;
	return *this;
}

// Same as this->rotateOnZ(Math.toRadians(90)), but faster
Vector& Vector::turnLeftOnZ() {
	const double temp = this->x;
	this->x = -this->y;
	this->y = temp;
	return *this;
}

// Same as this->rotateOnZ(Math.toRadians(-90)), but faster
Vector& Vector::turnRightOnZ() {
	const double temp = this->x;
	this->x = this->y;
	this->y = -temp;
	return *this;
}

Vector& Vector::invert() {
	this->x = -this->x;
	this->y = -this->y;
	this->z = -this->z;
	return *this;
}

Vector& Vector::add(const Vector &op2) {
	this->x += op2.x;
	this->y += op2.y;
	this->z += op2.z;
	return *this;
}

Vector& Vector::add(const double &x, const double &y, const double &z) {
	this->x += x;
	this->y += y;
	this->z += z;
	return *this;
}

Vector& Vector::subtract(const Vector &op2) {
	this->x -= op2.x;
	this->y -= op2.y;
	this->z -= op2.z;
	return *this;
}

Vector& Vector::subtract(const double &x, const double &y, const double &z) {
	this->x -= x;
	this->y -= y;
	this->z -= z;
	return *this;
}

Vector& Vector::scalarProduct(const double &scalar) {
	this->x *= scalar;
	this->y *= scalar;
	this->z *= scalar;
	return *this;
}

Vector& Vector::scalarDivision(const double &scalar) {
	this->x /= scalar;
	this->y /= scalar;
	this->z /= scalar;
	return *this;
}

Vector& Vector::interpolate(const Vector &op2, const double &alpha) {
	this->x = ((1.0 - alpha) * this->x) + (alpha * op2.x);
	this->y = ((1.0 - alpha) * this->y) + (alpha * op2.y);
	this->z = ((1.0 - alpha) * this->z) + (alpha * op2.z);
	return *this;
}

bool Vector::isUnit() const {
	return this->squareLenght() == 1.0;
}

bool Vector::isPerpendicular(const Vector &r) const {
	return this->dot(r) == 0.0;
}

bool Vector::isNull() const {
	if (this->x == 0.0) {
		if (this->y == 0.0) {
			return this->z == 0.0;
		}
	}
	return false;
}

bool Vector::isLonger(const Vector &r) const {
	return this->squareLenght() > r.squareLenght();
}

bool Vector::isLonger(const double &lenght) const {
	return this->squareLenght() > lenght * lenght;
}

bool Vector::isShorter(const Vector &r) const {
	return this->squareLenght() < r.squareLenght();
}

bool Vector::isShorter(const double &lenght) const {
	return this->squareLenght() < lenght * lenght;
}
